/*
 * @(#) DbInit
 * 版权声明 厦门畅享信息技术有限公司, 版权所有 违者必究
 *
 * <br> Copyright:  Copyright (c) 2018
 * <br> Company:厦门畅享信息技术有限公司
 * <br> @author caijh
 * <br> 2018-04-26 20:27:33
 * <br> @version 1.0
 * ————————————————————————————————
 *    修改记录
 *    修改者：
 *    修改时间：
 *    修改原因：
 * ————————————————————————————————
 */

package com.sunsharing.sysdb;

import com.sunsharing.component.utils.base.DateUtils;
import com.sunsharing.component.utils.base.StringUtils;
import com.sunsharing.entity.DbVersion;
import lombok.extern.log4j.Log4j;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileFilter;
import java.util.*;

/**
 * Created by criss on 2017/3/1.
 */
@Log4j
public class SysDbInit implements ApplicationContextAware {

    @Resource
    private SysSqlFileExecutor sysSqlFileExecutor;
    /**
     * sql脚本记录库，可根据需求扩展字段
     */
    private static final String CREATE_TABLE_SQL =
            "  (`RUN_ID` varchar(32) NOT NULL DEFAULT ''  COMMENT 'sql脚本执行ID',\n"
                    + "  `SQL_DOCUMENT` varchar(30) DEFAULT NULL  COMMENT 'sql 文件夹名称 ',\n"
                    + "  `SQL_NAME` varchar(30) DEFAULT NULL  COMMENT 'sql 脚本文件名 ',\n"
                    + "  `CREATE_TIME` char(14) DEFAULT NULL COMMENT 'sql 脚本执行时间',\n"
                    + "  `VERSION_NUM` int(11) DEFAULT NULL COMMENT 'sql 脚本版本号',\n"
                    + "  `RUN_FLAG` int(11) DEFAULT NULL COMMENT 'sql 执行的是否成功 -1表示失败，0表示正在执行，1表示成功',\n"
                    + "  `ERROR_SQL` longtext DEFAULT NULL COMMENT 'sql 执行失败的语句',\n"
                    + "  PRIMARY KEY (`RUN_ID`)\n"
                    + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='数据库脚本运行日志'";//sql执行版本


    private String versionTableName = "T_DBINIT_SQL_RUN";//版本库默认名称
    private JdbcTemplate jdbcTemplate = null;


    public void init() {
        DbVersion versionConfig = DynamicDataSource.dbInit.getVersion();
        String document = "";//文件名
        int version = 0;// 当前版本号
        if (!StringUtils.isBlank(versionConfig.getTable())) {
            versionTableName = versionConfig.getTable();
        }
        try {
            String location = versionConfig.getLocation();//版本库数据源位置
            jdbcTemplate = DynamicDataSource.getTemplate(location);

            log.info("=====================数据初始化开始执行========================");
            jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS " + versionTableName + CREATE_TABLE_SQL);
            File files = new File(System.getProperty("user.dir") + "\\sql_shell");
            for (File f : files.listFiles()) {
                document = f.getName();
                log.info("【************开始初始化目录 " + document + " 中的文件*************】");
                //获取更新数据待执行sql文件
                List<File> sqls = getNeedUpdateSqls(f);
                for (File sql : sqls) {
                    String delSql = "delete from " + versionTableName + " where SQL_DOCUMENT = ? and RUN_FLAG != 1";
                    jdbcTemplate.update(delSql, document);

                    log.info("【SQL脚本需要更新，执行的文件为：" + sql.getName() + "】");
                    version = getVersionOfNumber(sql.getName());
                    String insertSql = "insert into " + versionTableName
                            + "(RUN_ID,SQL_DOCUMENT,SQL_NAME,CREATE_TIME,VERSION_NUM,RUN_FLAG)"
                            + " VALUES('" + StringUtils.generateUUID() + "',"
                            + "'" + document + "',"
                            + "'" + sql.getName() + "',"
                            + "'" + DateUtils.getDBString(new Date()) + "',"
                            + version + ","
                            + 0
                            + " )";
                    jdbcTemplate.execute(insertSql);

                    sysSqlFileExecutor.execute(sql);

                    String updateSql = "update " + versionTableName
                            + " set RUN_FLAG = 1 where "
                            + " SQL_DOCUMENT = ?"
                            + " and VERSION_NUM = ?";
                    jdbcTemplate.update(updateSql, new Object[]{document, version});
                    log.info("SQL脚本执行成功，执行的文件为：" + sql.getName());
                }
                log.info("【************目录 " + document + " 中的sql初始化完毕*************】\n\n");
            }
        } catch (Exception e) {
            String updateSql = "update " + versionTableName
                    + " set "
                    + " RUN_FLAG = -1 ,"
                    + " ERROR_SQL = ?"
                    + " where "
                    + " SQL_DOCUMENT = ?"
                    + " and VERSION_NUM = ?";
            jdbcTemplate.update(updateSql, new Object[]{sysSqlFileExecutor.getErrorSql(), document, version});
            log.error("数据库脚本初始化执行失败:", e);
            throw new RuntimeException();
        }
    }


    /**
     * 获取需要更新的sql脚本(对比当前已经执行的最新的sql版本)
     *
     * @return 符合条件的sql文件
     */
    private List getNeedUpdateSqls(File file) {
        int currentVersionNum = 000;
        try {
            String sql = "select VERSION_NUM from "
                    + versionTableName
                    + " where SQL_DOCUMENT = '"
                    + file.getName()
                    + "'and RUN_FLAG = 1 order by VERSION_NUM desc limit 1";
            List list = jdbcTemplate.queryForList(sql);
            if (list.size() > 0) {
                Map row = (Map) list.get(0);
                currentVersionNum = MapUtils.getInteger(row, "VERSION_NUM", 000);
            }
        } catch (Exception e) {
            if (e.getMessage().indexOf("doesn't exist") != -1 || e.getMessage().indexOf("表或视图不存在") != -1) {
                log.warn("数据库不存在，执行SQL脚本");
            } else {
                log.warn("无法读取" + versionTableName + " 信息，重新开始导入数据,错误信息为：" + e.getMessage());
            }
            throw e;
        }
        List fileList = new ArrayList();
        try {
            fileList = getFiles(file, currentVersionNum);
            Collections.sort(fileList, new Comparator<File>() {
                @Override
                public int compare(File o1, File o2) {
                    int versionNum1 = getVersionOfNumber(o1.getName());
                    int versionNum2 = getVersionOfNumber(o2.getName());
                    if (versionNum1 > versionNum2) {
                        return 1;
                    } else if (versionNum1 < versionNum2) {
                        return -1;
                    } else {
                        return 0;
                    }
                }
            });
            return fileList;
        } catch (Exception e) {
            log.warn("无法读取数据库更新文件信息,错误信息为：" + e.getMessage(), e);
        }
        return fileList;
    }

    /**
     * 获取版原始版本号（字符串）
     *
     * @param fileName sql文件名
     * @return 版本号（字符串）
     */
    private String getVersionOfString(String fileName) {
        int begin = fileName.lastIndexOf("_");
        int end = fileName.lastIndexOf(".");
        fileName = fileName.substring(begin + 1, end);
        return fileName;
    }

    /**
     * 获取sql版本号（数值型）
     *
     * @param fileName sql文件名
     * @return 版本号 （数值型）
     */
    private int getVersionOfNumber(String fileName) {
        String version = getVersionOfString(fileName);
        return new Integer(version);
    }

    /**
     * 获取最新版本的sql文件
     *
     * @param sqlDirFile        sql文件
     * @param currentVersionNum 当前版本
     * @return 文件列表
     */
    private List<File> getFiles(File sqlDirFile, final int currentVersionNum) throws Exception {
        File[] files = sqlDirFile.listFiles(new FileFilter() {

            @Override
            public boolean accept(File pathname) {
                int fileVersionNum = getVersionOfNumber(pathname.getName());
                if (fileVersionNum > currentVersionNum) {
                    return true;
                } else {
                    return false;
                }
            }
        });
        List<File> array = new ArrayList<File>();
        if (files != null) {
            for (int i = 0; i < files.length; i++) {
                array.add(files[i]);
            }
        }
        return array;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        init();
    }
}
